package com.dianrong.common.uniauth.client.custom;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import com.dianrong.common.uniauth.client.custom.model.AllDomainUserExtInfo;
import com.dianrong.common.uniauth.client.custom.model.ShareDomainAuthentication;
import com.dianrong.common.uniauth.client.custom.model.SingleDomainUserExtInfo;
import com.dianrong.common.uniauth.common.util.ReflectionUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 用于处理多域共享信息的情况 
 * @author wanglin
 */
@Slf4j
public final class ShareDomainCasAuthenticationProvider extends CasAuthenticationProvider{
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Authentication authenticate = super.authenticate(authentication);
		if (needShareDomainSupport(authenticate)) {
			CasAuthenticationToken orginalAuthentication = (CasAuthenticationToken)authenticate;
			grantAuthoritesMapperProcess(orginalAuthentication.getUserDetails());
			return new ShareDomainAuthentication(this.getKey(),  orginalAuthentication.getPrincipal(), orginalAuthentication.getCredentials(),
					orginalAuthentication.getAuthorities(), orginalAuthentication.getUserDetails(), orginalAuthentication.getAssertion());
		} else {
			return authenticate;
		}
	}
	
	public boolean needShareDomainSupport(Object obj){
		return obj instanceof CasAuthenticationToken;
	}
	
	public void grantAuthoritesMapperProcess(UserDetails userDetails){
		if (!(userDetails instanceof  UserExtInfo)) {
				return;
		}
		GrantedAuthoritiesMapper authoritiesMapper =  (GrantedAuthoritiesMapper)ReflectionUtils.getField(this, "authoritiesMapper");
		if (authoritiesMapper == null ) {
			log.warn("please check AuthenticationProvider implementation, whether there is a fied type of  GrantedAuthoritiesMapper not name of authoritiesMapper. GrantedAuthoritiesMapper is not effective");
			return;
		}
		
		UserExtInfo userExtInfo = (UserExtInfo)userDetails;
		SingleDomainUserExtInfo loginDomainUserExtInfo = userExtInfo.getLoginDomainUserExtInfo();
		if (loginDomainUserExtInfo != null) {
			userExtInfo.setLoginDomainUserExtInfo(replaceAuthorities(loginDomainUserExtInfo, authoritiesMapper.mapAuthorities(loginDomainUserExtInfo.getAuthorities())));
		}
		AllDomainUserExtInfo allDomainUserExtInfo = userExtInfo.getAllDomainUserExtInfo();
		if (allDomainUserExtInfo != null) {
			Set<String> allDomainCodes =  allDomainUserExtInfo.getAllDomainCode();
			for (String key : allDomainCodes) {
				SingleDomainUserExtInfo domainUserExtInfo = allDomainUserExtInfo.getUserDetail(key);
				allDomainUserExtInfo.replaceUserExtInfo(key, replaceAuthorities(domainUserExtInfo, authoritiesMapper.mapAuthorities(domainUserExtInfo.getAuthorities())));
			}
		}
	}
	
	private SingleDomainUserExtInfo replaceAuthorities(final SingleDomainUserExtInfo orginalOne, final Collection<? extends GrantedAuthority> newAuthorities){
		Assert.notNull(orginalOne);
		Collection<? extends GrantedAuthority>  _newAuthorities = newAuthorities == null? new ArrayList<GrantedAuthority>() : newAuthorities;
		return new SingleDomainUserExtInfo(orginalOne.getUsername(), orginalOne.getPassword(), orginalOne.isEnabled(), orginalOne.isAccountNonExpired(), orginalOne.isCredentialsNonExpired() ,
				orginalOne.isAccountNonLocked(), _newAuthorities, orginalOne.getId(), orginalOne.getUserDto(), orginalOne.getDomainDto(), orginalOne.getPermMap(), orginalOne.getPermDtoMap());
	}
}
